package org.test4j.mock.processor;

import com.squareup.javapoet.JavaFile;

import javax.annotation.processing.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.util.Set;

abstract class BaseProcessor<A extends Annotation> extends AbstractProcessor {
    private static Filer filer;

    private static Messager messager;

    private static Elements elements;

    private boolean generated = false;

    @Override
    public synchronized void init(ProcessingEnvironment env) {
        super.init(env);
        this.filer = env.getFiler();
        this.messager = env.getMessager();
        this.elements = env.getElementUtils();
    }

    protected abstract Class<A> getAnnotationClass();

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        if (env.processingOver() || generated) {
            return true;
        }
        for (Element element : env.getRootElements()) {
            A annotation = element.getAnnotation(this.getAnnotationClass());
            if (annotation == null || !(element instanceof TypeElement)) {
                continue;
            }
            try {
                this.doProcessor(element, annotation);
            } catch (Throwable e) {
                error("generate mock files error:" + e.getMessage(), e);
            }
        }
        this.postProcessor();
        this.generated = true;
        return true;
    }

    public static TypeElement getTypeElement(String className) {
        return elements.getTypeElement(className);
    }

    protected abstract void doProcessor(Element element, A annotation);

    protected void postProcessor() {
    }

    public static void error(String info) {
        messager.printMessage(Diagnostic.Kind.ERROR, info);
    }

    public static void error(String info, Throwable e) {
        error(info + ":" + toString(e));
    }

    public static void writeFiler(JavaFile javaFile) {
        try {
            javaFile.writeTo(filer);
        } catch (IOException ie) {
            error("Failed to generate java file: " + ie.getMessage(), ie);
        }
    }

    /**
     * 将异常日志转换为字符串
     *
     * @param e
     * @return
     */
    static String toString(Throwable e) {
        if (e == null) {
            return "<null>";
        }
        try (StringWriter writer = new StringWriter(); PrintWriter print = new PrintWriter(writer)) {
            e.printStackTrace(print);
            return writer.toString();
        } catch (IOException ex) {
            ex.printStackTrace();
            return e.getMessage();
        }
    }
}